package com.example.demo.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.common.Result;
import com.example.demo.entity.dto.LoginerDTO;
import com.example.demo.listener.event.SysLogEvent;
import com.example.demo.queue.message.TestEvent;
import com.example.demo.queue.producer.TestEventProducer;
import com.example.demo.service.AsyncService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.*;

@RestController
@RequestMapping("/api/async")
@Api(tags = "4、多线程管理")
@ApiSupport(author = "罗海林", order = 4)
@Validated
@Slf4j
@RequiredArgsConstructor
public class AsyncController {

    private final AsyncService asyncService;
    private final ApplicationContext applicationContext;
    private final TestEventProducer testEventProducer;

    @PostMapping("/sendEmail")
    @ApiOperation(value = "发送QQ邮件", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 1)
    public Result sendEmail() {
        asyncService.email();

        Result res = new Result();
        res.ok();
        log.info("线程结束，等待邮件通知");
        return res;
    }

    @PostMapping("/getEmail")
    @ApiOperation(value = "获取邮件内容", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 2)
    public Result getEmail() {
        log.info("异步准备执行");
        Future<String> future = asyncService.getEmail();
        log.info("主线程继续执行");

        //有返回值的异步方法适合于多个线程同时执行，却又需要都出结果的场景
        try {
            while (!future.isDone()) {
                log.info("等待中");
                Thread.sleep(500);
            }
            log.info("线程返回值：" + future.get());
        }
        catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        Result res = new Result();
        res.ok();
        return res;
    }

    @PostMapping("/getInfo")
    @ApiOperation(value = "并行执行线程1", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 3)
    public Result getInfo() {
        log.info("异步准备执行");
        CountDownLatch latch = new CountDownLatch(2);

        Future<String> future1 = asyncService.getInfo1(latch);
        Future<String> future2 = asyncService.getInfo2(latch);

        log.info("主线程继续执行");

        try {
            //latch只监测相应线程是否执行完，或只等待指定时间（此时与线程是否执行完无关）
            latch.await(2, TimeUnit.SECONDS);
            log.info("latch等待结束");

            //future1.get()是阻塞的
            String str1 = future1.get();
            String str2 = future2.get();
            log.info(str1 + " | " + str2);
        } catch (InterruptedException | ExecutionException e) {
            future1.cancel(true);
            future2.cancel(true);
            e.printStackTrace();
        }

        Result res = new Result();
        res.ok();
        return res;
    }

    @PostMapping("/getInfo3")
    @ApiOperation(value = "有返回值时的多线程", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 4)
    public Result getInfo3() {
        log.info("异步准备执行");

        // 有join时会阻塞，没有join时不会阻塞
        CompletableFuture<String> future = asyncService.getInfo3();
        //future.join();

        log.info("主线程继续执行");

        Result res = new Result();
        res.ok();
        res.put("age", 38);
        return res;
    }

    @PostMapping("/getInfo4")
    @ApiOperation(value = "res是否每次不同", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 5)
    public Result getInfo4() {
        Result res = new Result();
        res.ok();
        res.put("name", "罗海林");
        return res;
    }

    @PostMapping("/testMaxAsyncCount")
    @ApiOperation(value = "测试最大线程运行数", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 6)
    public Result testMaxAsyncCount() {
        for (int i = 0; i < 50; i++) {
            JSONObject js = new JSONObject();
            js.put("i:", i);
            ApplicationEvent event = new SysLogEvent(js);
            applicationContext.publishEvent(event);
        }

        Result res = new Result().ok();
        return res;
    }

    @PostMapping("/testMaxDisruptor")
    @ApiOperation(value = "测试最大队列运行数", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 7)
    public Result testMaxDisruptor() {
        for (int i = 0; i < 50; i++) {
            TestEvent event = new TestEvent();
            event.setId(String.valueOf(i));
            event.setName("全晴" + i);
            if (i % 5 == 0) {
                LoginerDTO user = new LoginerDTO();
                user.setUsername("lhlysyl");
                user.setPassword("luohailin214510115");

                event.setType(200);
                event.setData(user);
            }
            else {
                JSONObject js = new JSONObject();
                js.put("id", i);
                js.put("name", "林春丽" + i);

                event.setType(404);
                event.setData(js);
            }

            testEventProducer.startTest(event);
        }

        Result res = new Result().ok();
        return res;
    }
}
